package cn.doourbest.learnnetty.learnnetty.handler;

import cn.doourbest.learnnetty.learnnetty.pojo.HttpProxyServerConfig;
import cn.doourbest.learnnetty.learnnetty.util.BSUtil;
import cn.doourbest.learnnetty.learnnetty.util.CertPool;
import cn.doourbest.learnnetty.learnnetty.util.ProtoUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.ReferenceCountUtil;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;


public class HttpProxyServerHandle extends ChannelInboundHandlerAdapter {

    private HttpProxyServerConfig serverConfig;
    private String host;
    private int port;
    private ChannelFuture sync;
    private List<Object> requestList = new ArrayList<>();

    public HttpProxyServerHandle(HttpProxyServerConfig serverConfig) {
        this.serverConfig = serverConfig;
    }

    //http代理隧道握手成功
    public final static HttpResponseStatus SUCCESS = new HttpResponseStatus(200,
            "Connection established");

    @Override
    public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception {

        if(msg instanceof HttpRequest){
            HttpRequest httpRequestMsg =  (HttpRequest) msg;
            if("CONNECT".equals(httpRequestMsg.method() == null ? null : httpRequestMsg.method().toString())){

                ProtoUtil.RequestProto requestProto = ProtoUtil.getRequestProto((HttpRequest) msg);
                this.host = requestProto.getHost();
                this.port = requestProto.getPort();

                HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, SUCCESS);
                ctx.writeAndFlush(response);
                ctx.channel().pipeline().remove("httpCodec");
                ReferenceCountUtil.release(msg);
                return;
            }
            System.out.println("------------------");
            System.out.println("URI:  " + ((HttpRequest) msg).uri());
           ((HttpRequest) msg).headers().entries().forEach(x -> {System.out.println(x.getKey() +"  :  " +x.getValue());} );
            System.out.println("------------------");
            sendMsg(msg,ctx);

        }else if(msg instanceof HttpContent){
            if(msg.equals(LastHttpContent.EMPTY_LAST_CONTENT)){
                ReferenceCountUtil.release(msg);
                return;
            }
            sendMsg(msg,ctx);

        }else{
            if (serverConfig.isHandleSsl()) {
                ByteBuf byteBuf = (ByteBuf) msg;
                if (byteBuf.getByte(0) == 22) {// ssl握手
                    int port = ((InetSocketAddress) ctx.channel().localAddress()).getPort();
                    if(this.host == null){
                        return;
                    }
                    SslContext sslCtx = SslContextBuilder
                            .forServer(serverConfig.getServerPriKey(), CertPool.getCert(port, this.host, serverConfig)).build();
                    ctx.pipeline().addFirst("httpCodec", new HttpServerCodec());
                    ctx.pipeline().addFirst("sslHandle", sslCtx.newHandler(ctx.alloc()));
                    // 重新过一遍pipeline，拿到解密后的的http报文
                    ctx.pipeline().fireChannelRead(msg);
                }
            }
        }

    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }

    private void sendMsg(Object msg,ChannelHandlerContext ctx) throws InterruptedException {

        if(sync == null && msg instanceof HttpRequest) {

            sync = BSUtil.getHttpsBootStrap(serverConfig,host,port).connect(this.host, this.port).sync();
        }else{
            requestList.add(msg);
            return;
        }
        boolean success = sync.isSuccess();
        if (success) {
            Channel channel = sync.channel();
            BSUtil.putMap(channel, ctx.channel());
            channel.writeAndFlush(msg);
        }
    }

}
